---
title: Todo app with shared workspaces
description: How to model a todo workspace app with shared workspaces in LiveStore.
sidebar:
  label: Todo app with workspaces
  order: 2
---

import EventlogModelingDiagram from '../../../_assets/diagrams/eventlog-modeling.tldr?tldraw';
import UserSchemaSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/user.schema.ts?snippet'
import WorkspaceSchemaSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/workspace.schema.ts?snippet'
import WorkspaceStoreOptionsSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/workspace.store.ts?snippet'
import UserStoreOptionsSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/user.store.ts?snippet'
import AppRootSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/App.tsx?snippet'
import WorkspaceComponentSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/Workspace.tsx?snippet'
import WorkspaceListSnippet from '../../../_assets/code/data-modeling/todo-workspaces/multi-store/WorkspaceList.tsx?snippet'

Let's consider a fairly common application scenario: An app (in this case a todo app) with shared workspaces. For the sake of this guide, we'll keep things simple but you should be able to nicely extend this to a more complex app.

## Requirements

- There are multiple independent todo workspaces
- Each workspace is initially created by a single user
- Users can join the workspace by knowing the workspace id and get read and write access
- For simplicity, the user identity is chosen when the app initially starts (i.e. a username) but in a real app this would be handled by a proper auth setup

## Data model

- We are splitting up our data model into two kinds of stores (with respective eventlogs and SQLite databases): The `workspace` store and the `user` store.

### `workspace` store (one per workspace)

For the `workspace` store we have the following events:

- `workspaceCreated`
- `todoAdded`
- `todoCompleted`
- `todoDeleted`
- `userJoined`

And the following state model:

- `workspace` table (with a single row for the workspace itself)
- `todo` table (with one row per todo item)
- `member` table (with one row per user who has joined the workspace)

### `user` store (one per user)

For the `user` store we have the following events:

- `workspaceCreated`
- `workspaceJoined`

And the following state model:

- `user` table (with a single row for the user itself)

Note that the `workspaceCreated` event is used both in the `workspace` and the `user` store. This is because each eventlog should be "self-sufficient" and not rely on other eventlogs to be present to fulfill its purpose.

<EventlogModelingDiagram class="my-8" />

## Schemas

**User store:**

<UserSchemaSnippet />

**Workspace store:**

<WorkspaceSchemaSnippet />

## Using the Multi-Store API

Now that we've defined our schemas, let's set up the multi-store API to manage workspace and user stores dynamically.

:::caution[Experimental API]
This guide uses the [experimental multi-store API](/framework-integrations/react-integration#multi-store) which is still early in its development.

If you have feedback or questions about this API, please don't hesitate to comment on the [RFC](https://github.com/livestorejs/livestore/pull/585)
:::

### Store Configuration

First, define store options for each store type using [`storeOptions()`](/framework-integrations/react-integration#storeoptionsoptions):

**Workspace store:**

<WorkspaceStoreOptionsSnippet />

**User store:**

<UserStoreOptionsSnippet />

### App Setup

Create a [`StoreRegistry`](/framework-integrations/react-integration#new-storeregistryconfig) and provide it to your React app:

<AppRootSnippet />

### Accessing Stores

Use the [`useStore()`](/framework-integrations/react-integration#usestoreoptions) hook to access specific workspace instances:

<WorkspaceComponentSnippet />

### Loading Multiple Workspaces

To display all workspaces for a user, first load the user store to get their workspace list, then dynamically load each workspace:

<WorkspaceListSnippet />

## Further notes

To make this app more production-ready, we might want to do the following:
- Use a proper auth setup to enforce a trusted user identity
- Introduce a proper user invite process
- Introduce access levels (e.g. read-only, read-write)
- Introduce end-to-end encryption

### Individual todo stores for complex data

If each todo item has a lot of data (e.g. think of a GitHub/Linear issue with lots of details), it might make sense to split up each todo item into its own store.

This would create **3 store types** instead of 2:
- **User stores** (one per user) - unchanged
- **Workspace stores** (one per workspace) - only basic todo metadata
- **Todo stores** (one per todo item) - rich todo data

Your app would then have **N + M + K stores** total (N workspaces + M users + K todo items).

This pattern improves performance by only loading detailed todo data when specifically viewing that item, and prevents large todos from slowing down workspace syncing.
